home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / nntpcunx.c < prev    next >
C/C++ Source or Header  |  1996-05-15  |  36KB  |  1,365 lines

  1. /*
  2.  * Program:    Network News Transfer Protocol (NNTP) client routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    5 January 1993
  13.  * Last Edited:    15 May 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notices appear in all copies and that both the
  20.  * above copyright notices and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include "mail.h"
  38. #include "osdep.h"
  39. #include <ctype.h>
  40. #include <stdio.h>
  41. #include <errno.h>
  42. extern int errno;        /* just in case */
  43. #include <sys/stat.h>
  44. #include "smtp.h"
  45. #include "nntp.h"
  46. #include "nntpcunx.h"
  47. #include "rfc822.h"
  48. #include "misc.h"
  49. #include "newsrc.h"
  50.  
  51. /* NNTP mail routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER nntpdriver = {
  57.   "nntp",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   nntp_valid,            /* mailbox is valid for us */
  60.   nntp_parameters,        /* manipulate parameters */
  61.   nntp_find,            /* find mailboxes */
  62.   nntp_find_bboards,        /* find bboards */
  63.   nntp_find_all,        /* find all mailboxes */
  64.   nntp_find_all_bboards,    /* find all bboards */
  65.   nntp_subscribe,        /* subscribe to mailbox */
  66.   nntp_unsubscribe,        /* unsubscribe from mailbox */
  67.   nntp_subscribe_bboard,    /* subscribe to bboard */
  68.   nntp_unsubscribe_bboard,    /* unsubscribe from bboard */
  69.   nntp_create,            /* create mailbox */
  70.   nntp_delete,            /* delete mailbox */
  71.   nntp_rename,            /* rename mailbox */
  72.   nntp_mopen,            /* open mailbox */
  73.   nntp_close,            /* close mailbox */
  74.   nntp_fetchfast,        /* fetch message "fast" attributes */
  75.   nntp_fetchflags,        /* fetch message flags */
  76.   nntp_fetchstructure,        /* fetch message envelopes */
  77.   nntp_fetchheader,        /* fetch message header only */
  78.   nntp_fetchtext,        /* fetch message body only */
  79.   nntp_fetchbody,        /* fetch message body section */
  80.   nntp_setflag,            /* set message flag */
  81.   nntp_clearflag,        /* clear message flag */
  82.   nntp_search,            /* search for message based on criteria */
  83.   nntp_ping,            /* ping mailbox to see if still alive */
  84.   nntp_check,            /* check for new messages */
  85.   nntp_expunge,            /* expunge deleted messages */
  86.   nntp_copy,            /* copy messages to another mailbox */
  87.   nntp_move,            /* move messages to another mailbox */
  88.   nntp_append,            /* append string message to mailbox */
  89.   nntp_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM nntpproto = {&nntpdriver};
  94.  
  95. /* NNTP mail validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *nntp_valid (name)
  101.     char *name;
  102. {
  103.                 /* must be bboard */
  104.   return *name == '*' ? mail_valid_net (name,&nntpdriver,NIL,NIL) : NIL;
  105. }
  106.  
  107.  
  108. /* News manipulate driver parameters
  109.  * Accepts: function code
  110.  *        function-dependent value
  111.  * Returns: function-dependent return value
  112.  */
  113.  
  114. void *nntp_parameters (function,value)
  115.     long function;
  116.     void *value;
  117. {
  118.   return NIL;
  119. }
  120.  
  121. /* NNTP mail find list of mailboxes
  122.  * Accepts: mail stream
  123.  *        pattern to search
  124.  */
  125.  
  126. void nntp_find (stream,pat)
  127.     MAILSTREAM *stream;
  128.     char *pat;
  129. {
  130.   /* Always a no-op */
  131. }
  132.  
  133.  
  134. /* NNTP mail find list of bboards
  135.  * Accepts: mail stream
  136.  *        pattern to search
  137.  */
  138.  
  139. void nntp_find_bboards (stream,pat)
  140.     MAILSTREAM *stream;
  141.     char *pat;
  142. {
  143.                 /* use .newsrc if a stream given */
  144.   if (stream && !stream->anonymous) newsrc_find (pat);
  145. }
  146.  
  147. /* NNTP mail find list of all mailboxes
  148.  * Accepts: mail stream
  149.  *        pattern to search
  150.  */
  151.  
  152. void nntp_find_all (stream,pat)
  153.     MAILSTREAM *stream;
  154.     char *pat;
  155. {
  156.   /* Always a no-op */
  157. }
  158.  
  159.  
  160. /* NNTP mail find list of all bboards
  161.  * Accepts: mail stream
  162.  *        pattern to search
  163.  */
  164.  
  165. void nntp_find_all_bboards (stream,pat)
  166.     MAILSTREAM *stream;
  167.     char *pat;
  168. {
  169.   char *s,*t,*bbd,*patx,tmp[MAILTMPLEN];
  170.                 /* use .newsrc if a stream given */
  171.   if (stream && LOCAL && LOCAL->nntpstream) {
  172.                 /* begin with a host specification? */
  173.     if (((*pat == '{') || ((*pat == '*') && (pat[1] == '{'))) &&
  174.     (t = strchr (pat,'}')) && *(patx = ++t)) {
  175.       if (*pat == '*') pat++;    /* yes, skip leading * (old Pine behavior) */
  176.       strcpy (tmp,pat);        /* copy host name */
  177.       bbd = tmp + (patx - pat);    /* where we write the bboards */
  178.     }
  179.     else {            /* no host specification */
  180.       bbd = tmp;        /* no prefix */
  181.       patx = pat;        /* use entire specification */
  182.     }
  183.                 /* ask server for all active newsgroups */
  184.     if (!(nntp_send (LOCAL->nntpstream,"LIST","ACTIVE") == NNTPGLIST)) return;
  185.                 /* process data until we see final dot */
  186.     while ((s = tcp_getline (LOCAL->nntpstream->tcpstream)) && *s != '.') {
  187.                 /* tie off after newsgroup name */
  188.       if (t = strchr (s,' ')) *t = '\0';
  189.       if (pmatch (s,patx)) {    /* report to main program if have match */
  190.     strcpy (bbd,s);        /* write newsgroup name after prefix */
  191.     mm_bboard (tmp);
  192.       }
  193.       fs_give ((void **) &s);    /* clean up */
  194.     }
  195.   }
  196. }
  197.  
  198. /* NNTP mail subscribe to mailbox
  199.  * Accepts: mail stream
  200.  *        mailbox to add to subscription list
  201.  * Returns: T on success, NIL on failure
  202.  */
  203.  
  204. long nntp_subscribe (stream,mailbox)
  205.     MAILSTREAM *stream;
  206.     char *mailbox;
  207. {
  208.   return NIL;            /* never valid for NNTP */
  209. }
  210.  
  211.  
  212. /* NNTP mail unsubscribe to mailbox
  213.  * Accepts: mail stream
  214.  *        mailbox to delete from subscription list
  215.  * Returns: T on success, NIL on failure
  216.  */
  217.  
  218. long nntp_unsubscribe (stream,mailbox)
  219.     MAILSTREAM *stream;
  220.     char *mailbox;
  221. {
  222.   return NIL;            /* never valid for NNTP */
  223. }
  224.  
  225.  
  226. /* NNTP mail subscribe to bboard
  227.  * Accepts: mail stream
  228.  *        bboard to add to subscription list
  229.  * Returns: T on success, NIL on failure
  230.  */
  231.  
  232. long nntp_subscribe_bboard (stream,mailbox)
  233.     MAILSTREAM *stream;
  234.     char *mailbox;
  235. {
  236.   char *s = strchr (mailbox,'}');
  237.   return s ? newsrc_update (s+1,':') : NIL;
  238. }
  239.  
  240.  
  241. /* NNTP mail unsubscribe to bboard
  242.  * Accepts: mail stream
  243.  *        bboard to delete from subscription list
  244.  * Returns: T on success, NIL on failure
  245.  */
  246.  
  247. long nntp_unsubscribe_bboard (stream,mailbox)
  248.     MAILSTREAM *stream;
  249.     char *mailbox;
  250. {
  251.   char *s = strchr (mailbox,'}');
  252.   return s ? newsrc_update (s+1,'!') : NIL;
  253. }
  254.  
  255. /* NNTP mail create mailbox
  256.  * Accepts: mail stream
  257.  *        mailbox name to create
  258.  * Returns: T on success, NIL on failure
  259.  */
  260.  
  261. long nntp_create (stream,mailbox)
  262.     MAILSTREAM *stream;
  263.     char *mailbox;
  264. {
  265.   return NIL;            /* never valid for NNTP */
  266. }
  267.  
  268.  
  269. /* NNTP mail delete mailbox
  270.  *        mailbox name to delete
  271.  * Returns: T on success, NIL on failure
  272.  */
  273.  
  274. long nntp_delete (stream,mailbox)
  275.     MAILSTREAM *stream;
  276.     char *mailbox;
  277. {
  278.   return NIL;            /* never valid for NNTP */
  279. }
  280.  
  281.  
  282. /* NNTP mail rename mailbox
  283.  * Accepts: mail stream
  284.  *        old mailbox name
  285.  *        new mailbox name
  286.  * Returns: T on success, NIL on failure
  287.  */
  288.  
  289. long nntp_rename (stream,old,new)
  290.     MAILSTREAM *stream;
  291.     char *old;
  292.     char *new;
  293. {
  294.   return NIL;            /* never valid for NNTP */
  295. }
  296.  
  297. /* NNTP mail open
  298.  * Accepts: stream to open
  299.  * Returns: stream on success, NIL on failure
  300.  */
  301.  
  302. MAILSTREAM *nntp_mopen (stream)
  303.     MAILSTREAM *stream;
  304. {
  305.   long i,j,k;
  306.   long nmsgs = 0;
  307.   long unseen = 0;
  308.   char c = NIL,*s,*t,tmp[MAILTMPLEN];
  309.   NETMBX mb;
  310.   void *tcpstream;
  311.   SMTPSTREAM *nstream = NIL;
  312.                 /* return prototype for OP_PROTOTYPE call */
  313.   if (!stream) return &nntpproto;
  314.   mail_valid_net_parse (stream->mailbox,&mb);
  315.   if (!*mb.mailbox) strcpy (mb.mailbox,"general");
  316.   if (LOCAL) {            /* if recycle stream, see if changing hosts */
  317.     if (strcmp (lcase (mb.host),lcase (strcpy (tmp,LOCAL->host)))) {
  318.       sprintf (tmp,"Closing connection to %s",LOCAL->host);
  319.       if (!stream->silent) mm_log (tmp,(long) NIL);
  320.     }
  321.     else {            /* same host, preserve NNTP connection */
  322.       sprintf (tmp,"Reusing connection to %s",LOCAL->host);
  323.       if (!stream->silent) mm_log (tmp,(long) NIL);
  324.       nstream = LOCAL->nntpstream;
  325.       LOCAL->nntpstream = NIL;    /* keep nntp_close() from punting it */
  326.     }
  327.     nntp_close (stream);    /* do close action */
  328.     stream->dtb = &nntpdriver;/* reattach this driver */
  329.     mail_free_cache (stream);    /* clean up cache */
  330.   }
  331.                 /* in case /debug switch given */
  332.   if (mb.dbgflag) stream->debug = T;
  333.  
  334.   if (!nstream) {        /* open NNTP now if not already open */
  335.     char *hostlist[2];
  336.     hostlist[0] = strcpy (tmp,mb.host);
  337.     if (mb.port) sprintf (tmp + strlen (tmp),":%ld",mb.port);
  338.     hostlist[1] = NIL;
  339.     nstream = nntp_open (hostlist,OP_READONLY+(stream->debug ? OP_DEBUG:NIL));
  340.   }
  341.   if (nstream) {        /* now try to open newsgroup */
  342.     if ((!stream->halfopen) &&    /* open the newsgroup if not halfopen */
  343.     ((nntp_send (nstream,"GROUP",mb.mailbox) != NNTPGOK) ||
  344.      ((k = strtol (nstream->reply + 4,&s,10)) < 0) ||
  345.      ((i = strtol (s,&s,10)) < 0) || ((j = strtol (s,&s,10)) < 0) ||
  346.      (nmsgs = i | j ? 1 + j - i : 0) < 0)) {
  347.       mm_log (nstream->reply,ERROR);
  348.       smtp_close (nstream);    /* punt stream */
  349.       nstream = NIL;
  350.       return NIL;
  351.     }
  352.                 /* newsgroup open, instantiate local data */
  353.     stream->local = fs_get (sizeof (NNTPLOCAL));
  354.     LOCAL->nntpstream = nstream;
  355.     LOCAL->dirty = NIL;        /* no update to .newsrc needed yet */
  356.                 /* copy host and newsgroup name */
  357.     LOCAL->host = cpystr (mb.host);
  358.     LOCAL->name = cpystr (mb.mailbox);
  359.     stream->sequence++;        /* bump sequence number */
  360.     stream->rdonly = T;        /* make sure higher level knows readonly */
  361.     LOCAL->number = NIL;
  362.     LOCAL->header = LOCAL->body = NIL;
  363.     LOCAL->buf = NIL;
  364.  
  365.     if (!stream->halfopen) {    /* if not half-open */
  366.       if (nmsgs) {        /* find what messages exist */
  367.     LOCAL->number = (unsigned long *) fs_get (nmsgs*sizeof(unsigned long));
  368.     sprintf (tmp,"%ld-%ld",i,j);
  369.     if ((nntp_send (nstream,"LISTGROUP",mb.mailbox) == NNTPGOK) ||
  370.         (nntp_send (nstream,"XHDR Date",tmp) == NNTPHEAD)) {
  371.       for (i = 0; (s = tcp_getline (nstream->tcpstream)) && strcmp (s,".");
  372.            i++) {        /* initialize c-client/NNTP map */
  373.         if (i < nmsgs) LOCAL->number[i] = atol (s);
  374.         fs_give ((void **) &s);
  375.       }
  376.       if (s) fs_give ((void **) &s);
  377.       if (i != nmsgs) {    /* found holes? */
  378.         if (nmsgs = i)
  379.           fs_resize ((void **) &LOCAL->number,nmsgs*sizeof(unsigned long));
  380.         else fs_give ((void **) &LOCAL->number);
  381.       }
  382.     }
  383.                 /* assume c-client/NNTP map is entire range */
  384.     else for (k = 0; k < nmsgs; ++k) LOCAL->number[k] = i + k;
  385.                 /* create caches */
  386.     LOCAL->header = (char **) fs_get (nmsgs * sizeof (char *));
  387.     LOCAL->body = (char **) fs_get (nmsgs * sizeof (char *));
  388.                 /* initialize per-message cache */
  389.     for (i = 0; i < nmsgs; ++i) LOCAL->header[i] = LOCAL->body[i] = NIL;
  390.       }
  391.                 /* make temporary buffer */
  392.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1);
  393.                 /* notify upper level that messages exist */
  394.       mail_exists (stream,nmsgs);
  395.                 /* read .newsrc entries */
  396.       mail_recent (stream,newsrc_read (LOCAL->name,stream,LOCAL->number));
  397.                 /* notify if empty bboard */
  398.       if (!(stream->nmsgs || stream->silent)) {
  399.     sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
  400.     mm_log (tmp,WARN);
  401.       }
  402.     }
  403.   }
  404.   return LOCAL ? stream : NIL;    /* if stream is alive, return to caller */
  405. }
  406.  
  407. /* NNTP mail close
  408.  * Accepts: MAIL stream
  409.  */
  410.  
  411. void nntp_close (stream)
  412.     MAILSTREAM *stream;
  413. {
  414.   if (LOCAL) {            /* only if a file is open */
  415.     nntp_check (stream);    /* dump final checkpoint */
  416.     if (LOCAL->name) fs_give ((void **) &LOCAL->name);
  417.     if (LOCAL->host) fs_give ((void **) &LOCAL->host);
  418.     nntp_gc (stream,GC_TEXTS);    /* free local cache */
  419.     if (LOCAL->number) fs_give ((void **) &LOCAL->number);
  420.     if (LOCAL->header) fs_give ((void **) &LOCAL->header);
  421.     if (LOCAL->body) fs_give ((void **) &LOCAL->body);
  422.                 /* free local scratch buffer */
  423.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  424.                 /* close NNTP connection */
  425.     if (LOCAL->nntpstream) smtp_close (LOCAL->nntpstream);
  426.                 /* nuke the local data */
  427.     fs_give ((void **) &stream->local);
  428.     stream->dtb = NIL;        /* log out the DTB */
  429.   }
  430. }
  431.  
  432. /* NNTP mail fetch fast information
  433.  * Accepts: MAIL stream
  434.  *        sequence
  435.  */
  436.  
  437. void nntp_fetchfast (stream,sequence)
  438.     MAILSTREAM *stream;
  439.     char *sequence;
  440. {
  441.   long i;
  442.   BODY *b;
  443.                 /* ugly and slow */
  444.   if (stream && LOCAL && mail_sequence (stream,sequence))
  445.     for (i = 1; i <= stream->nmsgs; i++)
  446.       if (mail_elt (stream,i)->sequence)
  447.     nntp_fetchstructure (stream,i,&b);
  448. }
  449.  
  450.  
  451. /* NNTP mail fetch flags
  452.  * Accepts: MAIL stream
  453.  *        sequence
  454.  */
  455.  
  456. void nntp_fetchflags (stream,sequence)
  457.     MAILSTREAM *stream;
  458.     char *sequence;
  459. {
  460.   return;            /* no-op for local mail */
  461. }
  462.  
  463. /* NNTP mail fetch envelope
  464.  * Accepts: MAIL stream
  465.  *        message # to fetch
  466.  *        pointer to return body
  467.  * Returns: envelope of this message, body returned in body value
  468.  *
  469.  * Fetches the "fast" information as well
  470.  */
  471.  
  472. ENVELOPE *nntp_fetchstructure (stream,msgno,body)
  473.     MAILSTREAM *stream;
  474.     long msgno;
  475.     BODY **body;
  476. {
  477.   char *h,*t;
  478.   LONGCACHE *lelt;
  479.   ENVELOPE **env;
  480.   STRING bs;
  481.   BODY **b;
  482.   unsigned long hdrsize;
  483.   unsigned long textsize = 0;
  484.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  485.   if (stream->scache) {        /* short cache */
  486.     if (msgno != stream->msgno){/* flush old poop if a different message */
  487.       mail_free_envelope (&stream->env);
  488.       mail_free_body (&stream->body);
  489.     }
  490.     stream->msgno = msgno;
  491.     env = &stream->env;        /* get pointers to envelope and body */
  492.     b = &stream->body;
  493.   }
  494.   else {            /* long cache */
  495.     lelt = mail_lelt (stream,msgno);
  496.     env = &lelt->env;        /* get pointers to envelope and body */
  497.     b = &lelt->body;
  498.   }
  499.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  500.     mail_free_envelope (env);    /* flush old envelope and body */
  501.     mail_free_body (b);
  502.     hdrsize = strlen (h = nntp_fetchheader (stream,msgno));
  503.     if (body) {            /* only if want to parse body */
  504.       textsize = strlen (t = nntp_fetchtext_work (stream,msgno));
  505.                 /* calculate message size */
  506.       elt->rfc822_size = hdrsize + textsize;
  507.       INIT (&bs,mail_string,(void *) t,textsize);
  508.     }
  509.                 /* parse envelope and body */
  510.     rfc822_parse_msg (env,body ? b : NIL,h,hdrsize,body ? &bs : NIL,BADHOST,
  511.               LOCAL->buf);
  512.                 /* parse date */
  513.     if (*env && (*env)->date) mail_parse_date (elt,(*env)->date);
  514.     if (!elt->month) mail_parse_date (elt,"01-JAN-1969 00:00:00 GMT");
  515.   }
  516.   if (body) *body = *b;        /* return the body */
  517.   return *env;            /* return the envelope */
  518. }
  519.  
  520. /* NNTP mail fetch message header
  521.  * Accepts: MAIL stream
  522.  *        message # to fetch
  523.  * Returns: message header in RFC822 format
  524.  */
  525.  
  526. char *nntp_fetchheader (stream,msgno)
  527.     MAILSTREAM *stream;
  528.     long msgno;
  529. {
  530.   char tmp[MAILTMPLEN];
  531.   long m = msgno - 1;
  532.   if (!LOCAL->header[m]) {    /* fetch header if don't have already */
  533.     sprintf (tmp,"%ld",LOCAL->number[m]);
  534.     if (nntp_send (LOCAL->nntpstream,"HEAD",tmp) == NNTPHEAD)
  535.       LOCAL->header[m] = nntp_slurp (stream);
  536.                 /* failed, mark as deleted */
  537.     else mail_elt (stream,msgno)->deleted = T;
  538.   }
  539.   return LOCAL->header[m] ? LOCAL->header[m] : "";
  540. }
  541.  
  542.  
  543. /* NNTP mail fetch message text (only)
  544.     body only;
  545.  * Accepts: MAIL stream
  546.  *        message # to fetch
  547.  * Returns: message text in RFC822 format
  548.  */
  549.  
  550. char *nntp_fetchtext (stream,msgno)
  551.     MAILSTREAM *stream;
  552.     long msgno;
  553. {
  554.   long m = msgno - 1;
  555.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  556.   elt->seen = T;        /* mark as seen */
  557.   return nntp_fetchtext_work (stream,msgno);
  558. }
  559.  
  560.  
  561. /* NNTP mail fetch message text work
  562.  * Accepts: MAIL stream
  563.  *        message # to fetch
  564.  * Returns: message text in RFC822 format
  565.  */
  566.  
  567. char *nntp_fetchtext_work (stream,msgno)
  568.     MAILSTREAM *stream;
  569.     long msgno;
  570. {
  571.   char tmp[MAILTMPLEN];
  572.   long m = msgno - 1;
  573.   if (!LOCAL->body[m]) {    /* fetch body if don't have already */
  574.     sprintf (tmp,"%ld",LOCAL->number[m]);
  575.     if (nntp_send (LOCAL->nntpstream,"BODY",tmp) == NNTPBODY)
  576.       LOCAL->body[m] = nntp_slurp (stream);
  577.                 /* failed, mark as deleted */
  578.     else mail_elt (stream,msgno)->deleted = T;
  579.   }
  580.   return LOCAL->body[m] ? LOCAL->body[m] : "";
  581. }
  582.  
  583. /* NNTP mail slurp NNTP dot-terminated text
  584.  * Accepts: MAIL stream
  585.  * Returns: text
  586.  */
  587.  
  588. char *nntp_slurp (stream)
  589.     MAILSTREAM *stream;
  590. {
  591.   char *s,*t;
  592.   unsigned long i;
  593.   unsigned long bufpos = 0;
  594.   while (s = tcp_getline (LOCAL->nntpstream->tcpstream)) {
  595.     if (*s == '.') {        /* possible end of text? */
  596.       if (s[1]) t = s + 1;    /* pointer to true start of line */
  597.       else {
  598.     fs_give ((void **) &s);    /* free the line */
  599.     break;            /* end of data */
  600.       }
  601.     }
  602.     else t = s;            /* want the entire line */
  603.                 /* ensure have enough room */
  604.     if (LOCAL->buflen < (bufpos + (i = strlen (t)) + 5))
  605.       fs_resize ((void **) &LOCAL->buf,LOCAL->buflen += (MAXMESSAGESIZE + 1));
  606.                 /* copy the text */
  607.     strncpy (LOCAL->buf + bufpos,t,i);
  608.     bufpos += i;        /* set new buffer position */
  609.     LOCAL->buf[bufpos++] = '\015';
  610.     LOCAL->buf[bufpos++] = '\012';
  611.     fs_give ((void **) &s);    /* free the line */
  612.   }
  613.   LOCAL->buf[bufpos++] = '\015';/* add final newline */
  614.   LOCAL->buf[bufpos++] = '\012';
  615.   LOCAL->buf[bufpos++] = '\0';    /* tie off string with NUL */
  616.   return cpystr (LOCAL->buf);    /* return copy of collected string */
  617. }
  618.  
  619. /* NNTP fetch message body as a structure
  620.  * Accepts: Mail stream
  621.  *        message # to fetch
  622.  *        section specifier
  623.  *        pointer to length
  624.  * Returns: pointer to section of message body
  625.  */
  626.  
  627. char *nntp_fetchbody (stream,m,s,len)
  628.     MAILSTREAM *stream;
  629.     long m;
  630.     char *s;
  631.     unsigned long *len;
  632. {
  633.   BODY *b;
  634.   PART *pt;
  635.   unsigned long i;
  636.   char *base;
  637.   unsigned long offset = 0;
  638.   MESSAGECACHE *elt = mail_elt (stream,m);
  639.                 /* make sure have a body */
  640.   if (!(nntp_fetchstructure (stream,m,&b) && b && s && *s &&
  641.     ((i = strtol (s,&s,10)) > 0) &&
  642.     (base = nntp_fetchtext_work (stream,m))))
  643.     return NIL;
  644.   do {                /* until find desired body part */
  645.                 /* multipart content? */
  646.     if (b->type == TYPEMULTIPART) {
  647.       pt = b->contents.part;    /* yes, find desired part */
  648.       while (--i && (pt = pt->next));
  649.       if (!pt) return NIL;    /* bad specifier */
  650.                 /* note new body, check valid nesting */
  651.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  652.       offset = pt->offset;    /* get new offset */
  653.     }
  654.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  655.                 /* need to go down further? */
  656.     if (i = *s) switch (b->type) {
  657.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  658.       offset = b->contents.msg.offset;
  659.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  660.     case TYPEMULTIPART:        /* multipart, get next section */
  661.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  662.     default:            /* bogus subpart specification */
  663.       return NIL;
  664.     }
  665.   } while (i);
  666.                 /* lose if body bogus */
  667.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  668.   elt->seen = T;        /* mark as seen */
  669.   return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
  670.               b->size.ibytes,b->encoding);
  671. }
  672.  
  673. /* NNTP mail set flag
  674.  * Accepts: MAIL stream
  675.  *        sequence
  676.  *        flag(s)
  677.  */
  678.  
  679. void nntp_setflag (stream,sequence,flag)
  680.     MAILSTREAM *stream;
  681.     char *sequence;
  682.     char *flag;
  683. {
  684.   MESSAGECACHE *elt;
  685.   long i;
  686.   short f = nntp_getflags (stream,flag);
  687.   if (!f) return;        /* no-op if no flags to modify */
  688.                 /* get sequence and loop on it */
  689.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  690.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  691.       if (f&fSEEN) elt->seen=T;    /* set all requested flags */
  692.       if (f&fDELETED) {        /* deletion also purges the cache */
  693.     elt->deleted = T;    /* mark deleted */
  694.     LOCAL->dirty = T;    /* mark dirty */
  695.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  696.     if (LOCAL->body[i]) fs_give ((void **) &LOCAL->body[i]);
  697.       }
  698.       if (f&fFLAGGED) elt->flagged = T;
  699.       if (f&fANSWERED) elt->answered = T;
  700.     }
  701. }
  702.  
  703.  
  704. /* NNTP mail clear flag
  705.  * Accepts: MAIL stream
  706.  *        sequence
  707.  *        flag(s)
  708.  */
  709.  
  710. void nntp_clearflag (stream,sequence,flag)
  711.     MAILSTREAM *stream;
  712.     char *sequence;
  713.     char *flag;
  714. {
  715.   MESSAGECACHE *elt;
  716.   long i;
  717.   short f = nntp_getflags (stream,flag);
  718.   if (!f) return;        /* no-op if no flags to modify */
  719.                 /* get sequence and loop on it */
  720.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  721.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  722.                 /* clear all requested flags */
  723.       if (f&fSEEN) elt->seen = NIL;
  724.       if (f&fDELETED) {
  725.     elt->deleted = NIL;    /* undelete */
  726.     LOCAL->dirty = T;    /* mark stream as dirty */
  727.       }
  728.       if (f&fFLAGGED) elt->flagged = NIL;
  729.       if (f&fANSWERED) elt->answered = NIL;
  730.     }
  731. }
  732.  
  733. /* NNTP mail search for messages
  734.  * Accepts: MAIL stream
  735.  *        search criteria
  736.  */
  737.  
  738. void nntp_search (stream,criteria)
  739.     MAILSTREAM *stream;
  740.     char *criteria;
  741. {
  742.   long i,n;
  743.   char *d;
  744.   search_t f;
  745.                 /* initially all searched */
  746.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  747.                 /* get first criterion */
  748.   if (criteria && (criteria = strtok (criteria," "))) {
  749.                 /* for each criterion */
  750.     for (; criteria; (criteria = strtok (NIL," "))) {
  751.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  752.       switch (*ucase (criteria)) {
  753.       case 'A':            /* possible ALL, ANSWERED */
  754.     if (!strcmp (criteria+1,"LL")) f = nntp_search_all;
  755.     else if (!strcmp (criteria+1,"NSWERED")) f = nntp_search_answered;
  756.     break;
  757.       case 'B':            /* possible BCC, BEFORE, BODY */
  758.     if (!strcmp (criteria+1,"CC"))
  759.       f = nntp_search_string (nntp_search_bcc,&d,&n);
  760.     else if (!strcmp (criteria+1,"EFORE"))
  761.       f = nntp_search_date (nntp_search_before,&n);
  762.     else if (!strcmp (criteria+1,"ODY"))
  763.       f = nntp_search_string (nntp_search_body,&d,&n);
  764.     break;
  765.       case 'C':            /* possible CC */
  766.     if (!strcmp (criteria+1,"C"))
  767.       f = nntp_search_string (nntp_search_cc,&d,&n);
  768.     break;
  769.       case 'D':            /* possible DELETED */
  770.     if (!strcmp (criteria+1,"ELETED")) f = nntp_search_deleted;
  771.     break;
  772.       case 'F':            /* possible FLAGGED, FROM */
  773.     if (!strcmp (criteria+1,"LAGGED")) f = nntp_search_flagged;
  774.     else if (!strcmp (criteria+1,"ROM"))
  775.       f = nntp_search_string (nntp_search_from,&d,&n);
  776.     break;
  777.       case 'K':            /* possible KEYWORD */
  778.     if (!strcmp (criteria+1,"EYWORD"))
  779.       f = nntp_search_flag (nntp_search_keyword,&d);
  780.     break;
  781.       case 'N':            /* possible NEW */
  782.     if (!strcmp (criteria+1,"EW")) f = nntp_search_new;
  783.     break;
  784.  
  785.       case 'O':            /* possible OLD, ON */
  786.     if (!strcmp (criteria+1,"LD")) f = nntp_search_old;
  787.     else if (!strcmp (criteria+1,"N"))
  788.       f = nntp_search_date (nntp_search_on,&n);
  789.     break;
  790.       case 'R':            /* possible RECENT */
  791.     if (!strcmp (criteria+1,"ECENT")) f = nntp_search_recent;
  792.     break;
  793.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  794.     if (!strcmp (criteria+1,"EEN")) f = nntp_search_seen;
  795.     else if (!strcmp (criteria+1,"INCE"))
  796.       f = nntp_search_date (nntp_search_since,&n);
  797.     else if (!strcmp (criteria+1,"UBJECT"))
  798.       f = nntp_search_string (nntp_search_subject,&d,&n);
  799.     break;
  800.       case 'T':            /* possible TEXT, TO */
  801.     if (!strcmp (criteria+1,"EXT"))
  802.       f = nntp_search_string (nntp_search_text,&d,&n);
  803.     else if (!strcmp (criteria+1,"O"))
  804.       f = nntp_search_string (nntp_search_to,&d,&n);
  805.     break;
  806.       case 'U':            /* possible UN* */
  807.     if (criteria[1] == 'N') {
  808.       if (!strcmp (criteria+2,"ANSWERED")) f = nntp_search_unanswered;
  809.       else if (!strcmp (criteria+2,"DELETED")) f = nntp_search_undeleted;
  810.       else if (!strcmp (criteria+2,"FLAGGED")) f = nntp_search_unflagged;
  811.       else if (!strcmp (criteria+2,"KEYWORD"))
  812.         f = nntp_search_flag (nntp_search_unkeyword,&d);
  813.       else if (!strcmp (criteria+2,"SEEN")) f = nntp_search_unseen;
  814.     }
  815.     break;
  816.       default:            /* we will barf below */
  817.     break;
  818.       }
  819.       if (!f) {            /* if can't determine any criteria */
  820.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  821.     mm_log (LOCAL->buf,ERROR);
  822.     return;
  823.       }
  824.                 /* run the search criterion */
  825.       for (i = 1; i <= stream->nmsgs; ++i)
  826.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  827.       mail_elt (stream,i)->searched = NIL;
  828.     }
  829.                 /* report search results to main program */
  830.     for (i = 1; i <= stream->nmsgs; ++i)
  831.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  832.   }
  833. }
  834.  
  835. /* NNTP mail ping mailbox
  836.  * Accepts: MAIL stream
  837.  * Returns: T if stream alive, else NIL
  838.  */
  839.  
  840. long nntp_ping (stream)
  841.     MAILSTREAM *stream;
  842. {
  843.   /* Kludge alert: SMTPSOFTFATAL is 421 which is used in NNTP to mean ``No
  844.    * next article in this group''.  Hopefully, no NNTP server will send this
  845.    * in response to a STAT */
  846.   return (nntp_send (LOCAL->nntpstream,"STAT",NIL) != SMTPSOFTFATAL);
  847. }
  848.  
  849.  
  850. /* NNTP mail check mailbox
  851.  * Accepts: MAIL stream
  852.  */
  853.  
  854. void nntp_check (stream)
  855.     MAILSTREAM *stream;
  856. {
  857.                 /* never do if no updates */
  858.   if (LOCAL->dirty) newsrc_write (LOCAL->name,stream,LOCAL->number);
  859.   LOCAL->dirty = NIL;
  860. }
  861.  
  862. /* NNTP mail expunge mailbox
  863.  * Accepts: MAIL stream
  864.  */
  865.  
  866. void nntp_expunge (stream)
  867.     MAILSTREAM *stream;
  868. {
  869.   if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
  870. }
  871.  
  872.  
  873. /* NNTP mail copy message(s)
  874.     s;
  875.  * Accepts: MAIL stream
  876.  *        sequence
  877.  *        destination mailbox
  878.  * Returns: T if copy successful, else NIL
  879.  */
  880.  
  881. long nntp_copy (stream,sequence,mailbox)
  882.     MAILSTREAM *stream;
  883.     char *sequence;
  884.     char *mailbox;
  885. {
  886.   mm_log ("Copy not valid for NNTP",ERROR);
  887.   return NIL;
  888. }
  889.  
  890.  
  891. /* NNTP mail move message(s)
  892.     s;
  893.  * Accepts: MAIL stream
  894.  *        sequence
  895.  *        destination mailbox
  896.  * Returns: T if move successful, else NIL
  897.  */
  898.  
  899. long nntp_move (stream,sequence,mailbox)
  900.     MAILSTREAM *stream;
  901.     char *sequence;
  902.     char *mailbox;
  903. {
  904.   mm_log ("Move not valid for NNTP",ERROR);
  905.   return NIL;
  906. }
  907.  
  908.  
  909. /* NNTP mail append message from stringstruct
  910.  * Accepts: MAIL stream
  911.  *        destination mailbox
  912.  *        stringstruct of messages to append
  913.  * Returns: T if append successful, else NIL
  914.  */
  915.  
  916. long nntp_append (stream,mailbox,flags,date,message)
  917.     MAILSTREAM *stream;
  918.     char *mailbox;
  919.     char *flags;
  920.     char *date;
  921.                STRING *message;
  922. {
  923.   mm_log ("Append not valid for NNTP",ERROR);
  924.   return NIL;
  925. }
  926.  
  927. /* NNTP garbage collect stream
  928.  * Accepts: Mail stream
  929.  *        garbage collection flags
  930.  */
  931.  
  932. void nntp_gc (stream,gcflags)
  933.     MAILSTREAM *stream;
  934.     long gcflags;
  935. {
  936.   unsigned long i;
  937.   if (!stream->halfopen)     /* never on half-open stream */
  938.     if (gcflags & GC_TEXTS)    /* garbage collect texts? */
  939.                 /* flush texts from cache */
  940.       for (i = 0; i < stream->nmsgs; i++) {
  941.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  942.     if (LOCAL->body[i]) fs_give ((void **) &LOCAL->body[i]);
  943.       }
  944. }
  945.  
  946. /* Internal routines */
  947.  
  948.  
  949. /* Parse flag list
  950.  * Accepts: MAIL stream
  951.  *        flag list as a character string
  952.  * Returns: flag command list
  953.  */
  954.  
  955. short nntp_getflags (stream,flag)
  956.     MAILSTREAM *stream;
  957.     char *flag;
  958. {
  959.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  960.   short f = 0;
  961.   short i,j;
  962.   if (flag && *flag) {        /* no-op if no flag string */
  963.                 /* check if a list and make sure valid */
  964.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  965.       mm_log ("Bad flag list",ERROR);
  966.       return NIL;
  967.     }
  968.                 /* copy the flag string w/o list construct */
  969.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  970.     tmp[j] = '\0';
  971.     t = ucase (tmp);        /* uppercase only from now on */
  972.  
  973.     while (t && *t) {        /* parse the flags */
  974.       if (*t == '\\') {        /* system flag? */
  975.     switch (*++t) {        /* dispatch based on first character */
  976.     case 'S':        /* possible \Seen flag */
  977.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  978.       t += 4;        /* skip past flag name */
  979.       break;
  980.     case 'D':        /* possible \Deleted flag */
  981.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  982.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  983.       t += 7;        /* skip past flag name */
  984.       break;
  985.     case 'F':        /* possible \Flagged flag */
  986.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  987.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  988.       t += 7;        /* skip past flag name */
  989.       break;
  990.     case 'A':        /* possible \Answered flag */
  991.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  992.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  993.       t += 8;        /* skip past flag name */
  994.       break;
  995.     default:        /* unknown */
  996.       i = 0;
  997.       break;
  998.     }
  999.                 /* add flag to flags list */
  1000.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  1001.       }
  1002.       else {            /* no user flags yet */
  1003.     t = strtok (t," ");    /* isolate flag name */
  1004.     sprintf (err,"Unknown flag: %.80s",t);
  1005.     t = strtok (NIL," ");    /* get next flag */
  1006.     mm_log (err,ERROR);
  1007.       }
  1008.     }
  1009.   }
  1010.   return f;
  1011. }
  1012.  
  1013. /* Search support routines
  1014.  * Accepts: MAIL stream
  1015.  *        message number
  1016.  *        pointer to additional data
  1017.  *        pointer to temporary buffer
  1018.  * Returns: T if search matches, else NIL
  1019.  */
  1020.  
  1021. char nntp_search_all (stream,msgno,d,n)
  1022.     MAILSTREAM *stream;
  1023.     long msgno;
  1024.     char *d;
  1025.     long n;
  1026. {
  1027.   return T;            /* ALL always succeeds */
  1028. }
  1029.  
  1030.  
  1031. char nntp_search_answered (stream,msgno,d,n)
  1032.     MAILSTREAM *stream;
  1033.     long msgno;
  1034.     char *d;
  1035.     long n;
  1036. {
  1037.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1038. }
  1039.  
  1040.  
  1041. char nntp_search_deleted (stream,msgno,d,n)
  1042.     MAILSTREAM *stream;
  1043.     long msgno;
  1044.     char *d;
  1045.     long n;
  1046. {
  1047.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1048. }
  1049.  
  1050.  
  1051. char nntp_search_flagged (stream,msgno,d,n)
  1052.     MAILSTREAM *stream;
  1053.     long msgno;
  1054.     char *d;
  1055.     long n;
  1056. {
  1057.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1058. }
  1059.  
  1060.  
  1061. char nntp_search_keyword (stream,msgno,d,n)
  1062.     MAILSTREAM *stream;
  1063.     long msgno;
  1064.     char *d;
  1065.     long n;
  1066. {
  1067.   return NIL;            /* keywords not supported yet */
  1068. }
  1069.  
  1070.  
  1071. char nntp_search_new (stream,msgno,d,n)
  1072.     MAILSTREAM *stream;
  1073.     long msgno;
  1074.     char *d;
  1075.     long n;
  1076. {
  1077.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1078.   return (elt->recent && !elt->seen) ? T : NIL;
  1079. }
  1080.  
  1081. char nntp_search_old (stream,msgno,d,n)
  1082.     MAILSTREAM *stream;
  1083.     long msgno;
  1084.     char *d;
  1085.     long n;
  1086. {
  1087.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1088. }
  1089.  
  1090.  
  1091. char nntp_search_recent (stream,msgno,d,n)
  1092.     MAILSTREAM *stream;
  1093.     long msgno;
  1094.     char *d;
  1095.     long n;
  1096. {
  1097.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1098. }
  1099.  
  1100.  
  1101. char nntp_search_seen (stream,msgno,d,n)
  1102.     MAILSTREAM *stream;
  1103.     long msgno;
  1104.     char *d;
  1105.     long n;
  1106. {
  1107.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1108. }
  1109.  
  1110.  
  1111. char nntp_search_unanswered (stream,msgno,d,n)
  1112.     MAILSTREAM *stream;
  1113.     long msgno;
  1114.     char *d;
  1115.     long n;
  1116. {
  1117.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1118. }
  1119.  
  1120.  
  1121. char nntp_search_undeleted (stream,msgno,d,n)
  1122.     MAILSTREAM *stream;
  1123.     long msgno;
  1124.     char *d;
  1125.     long n;
  1126. {
  1127.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1128. }
  1129.  
  1130.  
  1131. char nntp_search_unflagged (stream,msgno,d,n)
  1132.     MAILSTREAM *stream;
  1133.     long msgno;
  1134.     char *d;
  1135.     long n;
  1136. {
  1137.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1138. }
  1139.  
  1140.  
  1141. char nntp_search_unkeyword (stream,msgno,d,n)
  1142.     MAILSTREAM *stream;
  1143.     long msgno;
  1144.     char *d;
  1145.     long n;
  1146. {
  1147.   return T;            /* keywords not supported yet */
  1148. }
  1149.  
  1150.  
  1151. char nntp_search_unseen (stream,msgno,d,n)
  1152.     MAILSTREAM *stream;
  1153.     long msgno;
  1154.     char *d;
  1155.     long n;
  1156. {
  1157.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1158. }
  1159.  
  1160. char nntp_search_before (stream,msgno,d,n)
  1161.     MAILSTREAM *stream;
  1162.     long msgno;
  1163.     char *d;
  1164.     long n;
  1165. {
  1166.   return (char) (nntp_msgdate (stream,msgno) < n);
  1167. }
  1168.  
  1169.  
  1170. char nntp_search_on (stream,msgno,d,n)
  1171.     MAILSTREAM *stream;
  1172.     long msgno;
  1173.     char *d;
  1174.     long n;
  1175. {
  1176.   return (char) (nntp_msgdate (stream,msgno) == n);
  1177. }
  1178.  
  1179.  
  1180. char nntp_search_since (stream,msgno,d,n)
  1181.     MAILSTREAM *stream;
  1182.     long msgno;
  1183.     char *d;
  1184.     long n;
  1185. {
  1186.                 /* everybody interprets "since" as .GE. */
  1187.   return (char) (nntp_msgdate (stream,msgno) >= n);
  1188. }
  1189.  
  1190.  
  1191. unsigned long nntp_msgdate (stream,msgno)
  1192.     MAILSTREAM *stream;
  1193.     long msgno;
  1194. {
  1195.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1196.                 /* get date if don't have it yet */
  1197.   if (!elt->day) nntp_fetchstructure (stream,msgno,NIL);
  1198.   return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
  1199. }
  1200.  
  1201.  
  1202. char nntp_search_body (stream,msgno,d,n)
  1203.     MAILSTREAM *stream;
  1204.     long msgno;
  1205.     char *d;
  1206.     long n;
  1207. {
  1208.   char *t = nntp_fetchtext_work (stream,msgno);
  1209.   return (t && search (t,(unsigned long) strlen (t),d,n));
  1210. }
  1211.  
  1212.  
  1213. char nntp_search_subject (stream,msgno,d,n)
  1214.     MAILSTREAM *stream;
  1215.     long msgno;
  1216.     char *d;
  1217.     long n;
  1218. {
  1219.   char *t = nntp_fetchstructure (stream,msgno,NIL)->subject;
  1220.   return t ? search (t,strlen (t),d,n) : NIL;
  1221. }
  1222.  
  1223.  
  1224. char nntp_search_text (stream,msgno,d,n)
  1225.     MAILSTREAM *stream;
  1226.     long msgno;
  1227.     char *d;
  1228.     long n;
  1229. {
  1230.   char *t = nntp_fetchheader (stream,msgno);
  1231.   return (t && search (t,strlen (t),d,n)) ||
  1232.     nntp_search_body (stream,msgno,d,n);
  1233. }
  1234.  
  1235. char nntp_search_bcc (stream,msgno,d,n)
  1236.     MAILSTREAM *stream;
  1237.     long msgno;
  1238.     char *d;
  1239.     long n;
  1240. {
  1241.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->bcc;
  1242.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1243.                 /* get text for address */
  1244.   rfc822_write_address (LOCAL->buf,a);
  1245.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1246. }
  1247.  
  1248.  
  1249. char nntp_search_cc (stream,msgno,d,n)
  1250.     MAILSTREAM *stream;
  1251.     long msgno;
  1252.     char *d;
  1253.     long n;
  1254. {
  1255.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->cc;
  1256.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1257.                 /* get text for address */
  1258.   rfc822_write_address (LOCAL->buf,a);
  1259.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1260. }
  1261.  
  1262.  
  1263. char nntp_search_from (stream,msgno,d,n)
  1264.     MAILSTREAM *stream;
  1265.     long msgno;
  1266.     char *d;
  1267.     long n;
  1268. {
  1269.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->from;
  1270.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1271.                 /* get text for address */
  1272.   rfc822_write_address (LOCAL->buf,a);
  1273.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1274. }
  1275.  
  1276.  
  1277. char nntp_search_to (stream,msgno,d,n)
  1278.     MAILSTREAM *stream;
  1279.     long msgno;
  1280.     char *d;
  1281.     long n;
  1282. {
  1283.   ADDRESS *a = nntp_fetchstructure (stream,msgno,NIL)->to;
  1284.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1285.                 /* get text for address */
  1286.   rfc822_write_address (LOCAL->buf,a);
  1287.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1288. }
  1289.  
  1290. /* Search parsers */
  1291.  
  1292.  
  1293. /* Parse a date
  1294.  * Accepts: function to return
  1295.  *        pointer to date integer to return
  1296.  * Returns: function to return
  1297.  */
  1298.  
  1299. search_t nntp_search_date (f,n)
  1300.     search_t f;
  1301.     long *n;
  1302. {
  1303.   long i;
  1304.   char *s;
  1305.   MESSAGECACHE elt;
  1306.                 /* parse the date and return fn if OK */
  1307.   return (nntp_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1308.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1309. }
  1310.  
  1311. /* Parse a flag
  1312.  * Accepts: function to return
  1313.  *        pointer to string to return
  1314.  * Returns: function to return
  1315.  */
  1316.  
  1317. search_t nntp_search_flag (f,d)
  1318.     search_t f;
  1319.     char **d;
  1320. {
  1321.                 /* get a keyword, return if OK */
  1322.   return (*d = strtok (NIL," ")) ? f : NIL;
  1323. }
  1324.  
  1325.  
  1326. /* Parse a string
  1327.  * Accepts: function to return
  1328.  *        pointer to string to return
  1329.  *        pointer to string length to return
  1330.  * Returns: function to return
  1331.  */
  1332.  
  1333. search_t nntp_search_string (f,d,n)
  1334.     search_t f;
  1335.     char **d;
  1336.     long *n;
  1337. {
  1338.   char *end = " ";
  1339.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1340.   if (!c) return NIL;        /* missing argument */
  1341.   switch (*c) {            /* see what the argument is */
  1342.   case '{':            /* literal string */
  1343.     *n = strtol (c+1,d,10);    /* get its length */
  1344.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1345.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1346.       char e = *--c;
  1347.       *c = DELIM;        /* make sure not a space */
  1348.       strtok (c," ");        /* reset the strtok mechanism */
  1349.       *c = e;            /* put character back */
  1350.       break;
  1351.     }
  1352.   case '\0':            /* catch bogons */
  1353.   case ' ':
  1354.     return NIL;
  1355.   case '"':            /* quoted string */
  1356.     if (strchr (c+1,'"')) end = "\"";
  1357.     else return NIL;
  1358.   default:            /* atomic string */
  1359.     if (*d = strtok (c,end)) *n = strlen (*d);
  1360.     else return NIL;
  1361.     break;
  1362.   }
  1363.   return f;
  1364. }
  1365.